/**
 *  Version:     @(#)aio_base.h    0.2.11 18/06/2007
 *
 *  Authors:     Hailong Xia <hlxxxx@gmail.com> 
 *               Jinyuan Hei <heijinyuan@htomail.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty 
 *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See 
 *  the GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General
 *  Public License along with this library; if not, write to the 
 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
 *  Boston, MA 02111-1307  USA
 */

#ifndef __AIO_BASE_H
#define __AIO_BASE_H

/**  
 * AIO base is a context while it should be allocated in user thread.
 */

#include "aio_queue.h"
#include "aio_event.h"
#include "aio_util.h"

/**
 * Aio log buffer size default is 2k. You can specify it as following: 
 */ 

#if 0 
# undef AIO_LOG_BUFSIZE
# define AIO_LOG_BUFSIZE (value)
#endif

/**
 * Aio slot's receive buffer offset default is 0.  
 * You can specify it as following:
 */ 

#if 0
# undef  AIO_SLOT_RECVBUF_OFFSET 
# define AIO_SLOT_RECVBUF_OFFSET (value)
#endif

/**
 * Aio slot's max send buffer capability default is 5 
 * (5 * AIO_BUF_PLSIZE).  You can specify it as following: 
 */ 

#if 0
# undef  AIO_SLOT_SENDQUE_CAPABILITY 
# define AIO_SLOT_SENDQUE_CAPABILITY (value)
#endif

/**
 * Aio slot's tcp listen backlog default is 500.  
 * You can specify it as following:
 */ 

#if 0
# undef  AIO_SLOT_DEFAULT_BACKLOG  
# define AIO_SLOT_DEFAULT_BACKLOG (value)
#endif

/**
 * You need to modify macro AIO_BUF_PLSIZE when your packet 
 * max length > 0x2000
 */
#define AIO_BUF_PLSIZE        0x2000 
#define AIO_BUF_LISTSIZE      5 
#define OFFSETOF(s, m)        (size_t)(&(((s *)0)->m))

#define ABEBP(ab)             ((ab)->eb)
#define ABUSRP(ab)            ((ab)->usrpointer)
#define ABASSIGN(ab)          ((ab)->refcnt++, (ab))

#ifdef AIO_NO_CACHE
# define AIO_BUF_ALLOC(ab)                                   \
        (struct aio_buf *)calloc(1, sizeof(struct aio_buf));
#else
# define AIO_BUF_ALLOC(ab)                                   \
        (ab)->tmpbufptr = !FREELIST_EMPTY(&(ab)->buf_head) ? \
        (struct aio_buf *)FREELIST_POP(&(ab)->buf_head) :    \
        (struct aio_buf *)malloc(sizeof(struct aio_buf));    \
        (ab)->tmpbufptr->head = &(ab)->buf_head;             \
        (ab)->tmpbufptr->offset = (ab)->tmpbufptr->length = 0;
#endif

#ifdef AIO_NO_CACHE
# define AIO_BUF_FREE(buf)  free((buf))                               
#else
# define AIO_BUF_FREE(buf)                                   \
do {                                                         \
        if ((buf) && !FREELIST_PUSH((buf)->head, (buf)))     \
                free((buf));                                 \
} while (0)
#endif

#define BOFF(buf)             ((buf)->offset)
#define BLEN(buf)             ((buf)->length)
#define BPTR(buf)             ((buf)->payload + (buf)->offset)
#define BEND(buf)             (BPTR(buf) + (buf)->length)
#define BSTR(buf)             ((char*)BPTR(buf))

#ifdef __cplusplus
extern "C" {
#endif

FREELIST_HEAD(buf_freelist, AIO_BUF_LISTSIZE); 

struct aio_buf 
{
#ifndef AIO_NO_CACHE
        struct buf_freelist    *head;             
#endif
        unsigned int           offset;    
        unsigned int           length;  
        unsigned char          payload[AIO_BUF_PLSIZE];
};

struct aio_timer {
        AIO_EVENT              ev;
        struct timeval         tv;
        void                   (*fn)(struct aio_timer *, void *);
        void                   *arg;
        LIST_ENTRY(aio_timer)  entries;
};

struct aio_base {
        AIO_EVBASE                         *eb;
        void                               *usrpointer;
        struct aio_buf                     *tmpbufptr;
        unsigned long                      refcnt;
        struct buf_freelist                buf_head;
        LIST_HEAD(aio_slotnode, aio_slot)  slot_head;
        LIST_HEAD(aio_timenode, aio_timer) timer_head;
};

/** 
 * You can assign argument eb when there is a exist event base.  
 * The argument size only is valid when the argument eb is NULL. 
 * The size indicate max fd count which aio can support.
 */
struct aio_base* aio_create(AIO_EVBASE *eb, int size);

/**  
 * If your event base isn't allocated in function aio_create, 
 * you shouldn't to destroy it. You can assign the argument 
 * cleanevbase to zero or non-zero to inform aio_destroy whether 
 * it will free event base. 
 */
void aio_destroy(struct aio_base *ab, int cleanevbase);

struct aio_buf*  aio_buf_alloc(struct aio_base *ab);

void aio_buf_free(struct aio_buf *buf);

struct aio_timer* aio_timer_set(struct aio_base *ab, unsigned msec, 
        void (*fn)(struct aio_timer *, void *), void *arg);

void aio_timer_cancel(struct aio_timer *at);

int aio_loop(struct aio_base* ab, unsigned msec);

#ifdef __cplusplus
}
#endif

#endif /* __AIO_BASE_H */ 

